Notes
Lines 17–19. Changing the number of counter digits at compile time
Surrounding the #define DIGITS at line 18 with #ifdef ... #endif makes it possible change the the value of DIGITS at compile time; i.e., it isn't necessary to edit the source code file to change this value.
When running the compiler from the command line, add the following switch:
-D=DIGITS=<value>
<value> should be in the range 1 to 5.
When running the compiler from BricxCC, enter the switch in the Switches edit box which is found on the NBC/NXC tab of the Compiler tab of the Preferences dialog.

Lines 22–24. Internal representation of the counter
These three variable declarations comprise a shared resource and its protective mutex.
The counter array maintains the current value of the count as an array of digits.
counter_changed is a flag that tells the show_count task when to update the NXT display.
Lines 28–45. Preprocessor literals
Be aware that a #define defines a textual substitution made in the source code by the preprocessor before the NXC compiler sees the code. This is the reason for what might otherwise looks to be overly elaborate use of parentheses.
Abbreviations used
H    height
HRZ    horizontal
VRT    vertical
W    width
X_OFFSET and Y_OFFSET are the displacements needed to center the count on the NXT's screen.
Lines 47–59. Segment drawing functions
These two inline functions do all the output to the NXT screen. The 7-segment digits you see are built up from multiple calls to these functions.  hrz_segment_out draws the horizontal segments; vrt_segment_out draws the vertical ones.
Lines 76–137. Segment output functions
Each of these functions computes the position of a particular segment in the 7-segment pattern and then calls one of segment drawing functions to draw it.
Lines 142–240. Digit output functions
The digits on the screen are built up from multiple calls to the segment output functions.
Spreading the work of displaying each of the 7-segment digits over many small functions makes the program a little longer, but has the advantage of making development easier, especially in the debugging and maintenance phases.
Line 245. offset function
Each digit of the count must be placed at its proper offset from the left side of the NXT screen. This function makes the necessary computation. It is called by the show_count task.
Line 254. button_bumped function
Bumped means pressed and then released. You can not get repeated increments or decrements by holding down a button.
Since this function is shared by three tasks, increment, decrement, and zero, you might think it should protected by declaring it a safecall function. That's a good thought, but it's not necessary in this case because all the calls take place within the protection of counter_mutex.
Line 268. increment task
The  main concern of the for-loop is carry propagation. Each time the right button is bumped, the units digit of the counter is increased by one. If it is at 9 when this happens, the units digit rolls over to 0 and a carry is generated, which must be propagated to the tens digit, and so on. To simplify the code, the for-loop is tricked into doing the units increment; this is why carry is initialized to 1.
The for-loop's termination condition ((carry == 1) && (i < DIGITS)) stops the carry propagation when there is no longer a carry to propagate or when the carry propagates beyond the highest order digit. In the latter event, the counter rolls over from all nines to all zeros.
Pattern for tasks sharing a resource
All the tasks in the counter program, except the main task, share a common design pattern. This pattern is something you should keep in mind because you will find it useful over and over again.
task <task_name>()
{
   while(true)
   {
      Acquire(<mutex_name>);
      /* code */
      Release(<mutex_name>);
      Yield();
   }
}

Line 297. decrement task
The decrement task is really just the increment task modified to do subtraction, i.e., to deal with borrow propagation.
Line 325. zero task
The zero task reinitializes the counter's global variables.
Line 342. show_count task
Although the code is spread over many lines, it is really very simple because all the work is done by the ten digit drawing functions, digit_0, ..., digit_9. The task is a good demonstration of the NXC switch statement. It could be replaced with a long chain of if-else statements but a switch statement is really much more readable and maintainable.
Line 395. main task
All this task does is initialize the counter's global variables. It then exits, allowing the other four tasks to start up. These will loop repeatedly until the gray button is pressed.